<?php


namespace App\Command\Task;


use App\Entity\ApiDoc;
use App\Utils\Annotation\ApiAuthority;
use App\Utils\Annotation\ApiName;
use App\Utils\Annotation\ApiParam;
use App\Utils\Helper\GetterHelper;
use App\Utils\Traits\ControllerTrait;
use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Routing\Annotation\Route;

class ApiDocCommand extends Command {
    use ControllerTrait;

    protected static $defaultName = 'task:api-doc';
    protected $controllerPath;
    protected $annotationReader;

    public function __construct (string $name = null) {
        parent::__construct($name);
        $this->controllerPath = GetterHelper::getParameter('kernel.controller_path');
        $this->annotationReader = new AnnotationReader();
    }

    protected function configure () {
        $this->setDescription('生成接口文档');
    }

    protected function execute (InputInterface $input, OutputInterface $output) {
        $result = [];

        $controllers = $this->getControllers();
        $progressBar = new ProgressBar($output, count($controllers));
        $progressBar->start();
        foreach ($controllers as $controller) {
            $class_item = [];
            $ref = new \ReflectionClass($controller);
            // 获取基础信息
            $base_router_prefix = $base_api_name = $base_api_description = '未知';
            foreach ($this->annotationReader->getClassAnnotations($ref) as $classAnnotation) {
                if ($classAnnotation instanceof Route) {
                    // 路由前缀
                    $base_router_prefix = $classAnnotation->getPath();
                }
                if ($classAnnotation instanceof ApiName) {
                    // 控制器名
                    $base_api_name = $classAnnotation->name;
                    $base_api_description = $classAnnotation->description;
                }
            }
            $class_item = [
                'base_router_prefix'   => $base_router_prefix,
                'base_api_name'        => $base_api_name,
                'base_api_description' => $base_api_description,
            ];

            // 获取方法参数
            foreach ($ref->getMethods() as $method) {
                if ($method->class == $controller && $method->isPublic() && !$this->isMagicFunction($method->name)) {
                    $method_item = [
                        'method_name'  => $method->name,
                        'method_class' => $method->class,
                    ];
                    $annotations = $this->annotationReader->getMethodAnnotations($method);
                    foreach ($annotations as $annotation) {
                        // 记录声明路由
                        if ($annotation instanceof Route) {
                            $method_item['router'] = [
                                'path'   => $base_router_prefix.$annotation->getPath(),
                                'method' => $annotation->getMethods(),
                                'name'   => $annotation->getName(),
                            ];
                        }
                        // 记录声明名称
                        if ($annotation instanceof ApiName) {
                            $method_item['api_name'] = (array)$annotation;
                        }
                        // 记录声明权限
                        if ($annotation instanceof ApiAuthority) {
                            $method_item['api_authority'] = (array)$annotation;
                        }
                        // 记录声明参数
                        if ($annotation instanceof ApiParam) {
                            $method_item['api_params'][] = (array)$annotation;
                        }
                    }

                    // 记录配置响应数据
                    if ($method->class) {
                        $api_response = $this->getResponse($method->class, $method->name);
                        if ($api_response) {
                            $method_item['api_response'][] = $api_response;
                        }
                    }

                    if (isset($method_item['api_name'])) {
                        $class_item['methods'][] = $method_item;
                    }
                }
            }
            $progressBar->advance();
            array_push($result, $class_item);
        }

        $api_doc = new ApiDoc();
        $api_doc->setContent(json_encode($result, JSON_UNESCAPED_UNICODE));
        $api_doc->setCreatedAt(new \DateTime());
        $this->getEntityManage()->persist($api_doc);
        $this->getEntityManage()->flush();

        $progressBar->finish();
    }

    /**
     * 获取对应方法的响应数据
     * @param $class
     * @param $method
     * @return mixed|null
     */
    protected function getResponse ($class, $method) {
        try {
            $result = null;
            $prefix = 'App\Utils\API\Response\\';
            // 获取class信息
            $class_info = explode('\\', $class);
            $class_info[count($class_info) - 1] = str_replace('Controller', 'Response', $class_info[count($class_info) - 1]);
            unset($class_info[0],$class_info[1]);
            // 获取对应配置信息
            $_class = $prefix.implode('\\', $class_info);
            if (class_exists($_class)){
                $response_class = new $_class();
                $data = $response_class->getData();
                if (isset($data[$method])) {
                    $result = $data[$method];
                }
            }
            return $result;
        } catch (\Exception $e) {

        }
    }

    /**
     * 判断方法名是否为魔术方法
     * @param $method_name
     * @return false|int
     */
    protected function isMagicFunction ($method_name) {
        $result = false;

        if (preg_match('/^(__).*$/', $method_name)) {
            $result = true;
        }

        return $result;
    }

    /**
     * 获取controller集合
     * @return array
     */
    protected function getControllers () {
        $result = [];

        $finder = new Finder();
        $finder->files()->in($this->controllerPath);
        if ($finder->hasResults()) {
            $prefix = 'App\Controller\\';
            foreach ($finder as $file) {
                $class = rtrim(str_replace('/', '\\', $file->getRelativePathname()), '.php');
                array_push($result, $prefix.$class);
            }
        }

        return $result;
    }

}